import { ElNotification } from 'element-plus';
import { isRef, unref } from 'vue';
import { IRequestInit } from './fetch';
import { sortDeepObj } from './sort';

export interface IFetchCacheItem {
  callbackList: { resolve: any; reject: any }[];
  controller: AbortController;
  url: string;
  opts: IRequestInit;
}
export class FetchCache {
  /** 对象存储数组 */
  private list: IFetchCacheItem[] = [];
  /**
   * 保存一个fetch请求对象
   *
   * @param {string} url 请求url
   * @param {IRequestInit} opts 接口请求的配置
   * @param {cb<any>} 回调函数
   * @param {AbortController} controller fetch取消请求控制器
   * @return {*} void
   * @memberof FetchCache
   */
  add(url: string, opts: IRequestInit, cb: IFetchCacheItem['callbackList'][number], controller: AbortController) {
    const preRequest = this.find(url, opts);

    if (preRequest) {
      // 缓存当前请求的observable可订阅对象
      preRequest.callbackList.push(cb);
      // 取消上一次请求
      preRequest.controller.abort();
      // 保存最后一次请求的controller
      preRequest.controller = controller;
      return true;
    }

    this.list.push({
      url,
      opts,
      controller,
      callbackList: [cb]
    });
  }
  /**
   * 根据请求的 url 及 请求配置参数 查找 IFetchCacheItem 对象
   *
   * @param {string} url 请求url
   * @param {IRequestInit} opts 接口请求的配置
   * @return {*} IFetchCacheItem 对象
   * @memberof FetchCache
   */
  find(url: string, opts: IRequestInit) {
    return this.list.find((item) => {
      const originStr = sortDeepObj({
        url: item.url,
        method: item.opts.method,
        params: item.opts.params,
        body: item.opts.body,
        headers: item.opts.headers
      });
      const requestStr = sortDeepObj({
        url,
        params: opts.params,
        body: opts.body,
        headers: opts.headers,
        method: opts.method
      });
      // 对比对象字符串是否一致
      return JSON.stringify(originStr) === JSON.stringify(requestStr);
    });
  }
  /**
   * 判断是否存在指定url及options的请求
   *
   * @param {string} url 请求 url
   * @param {IRequestInit} opts 接口请求的配置
   * @return {boolean} boolean
   * @memberof FetchCache
   */
  has(url: string, opts: IRequestInit) {
    return !!this.find(url, opts);
  }
  /**
   * 移除缓存的fetch对象
   *
   * @param {(string | IFetchCacheItem)} [target] 请求 url 或者 IFetchCacheItem 对象
   * @param {IRequestInit} [opts] 接口请求的配置
   * @memberof FetchCache
   */
  remove(target?: string | IFetchCacheItem, opts?: IRequestInit) {
    let curRequest = target as IFetchCacheItem;
    if (typeof target === 'string') {
      curRequest = this.find(target, opts || {}) as IFetchCacheItem;
    }
    if (curRequest) {
      this.list.splice(this.list.indexOf(curRequest), 1);
    }
  }
}

/**
 * 解除vue响应式
 *
 * @export
 * @param {*} body 对象
 * @return {*} T
 */
export function unrefRecursion<T = any>(body: T) {
  for (let key in body) {
    if (isRef(body[key])) body[key] = unref(body[key]) as any;
    if (typeof body[key] === 'object') body[key] = unrefRecursion(body[key]);
  }
  return body;
}

const parseRes = async (res: Response) => {
  const contentType = res.headers.get('Content-Type');
  if (contentType) {
    if (contentType.indexOf('json') > -1) {
      return await res.json();
    }
    if (contentType.indexOf('text') > -1) {
      return await res.text();
    }
    if (contentType.indexOf('form') > -1) {
      return await res.formData();
    }
    if (contentType.indexOf('video') > -1) {
      return await res.blob();
    }
  }
  return await res.text();
};
/**
 * fetch 请求后参数处理
 * @param res 返回结果
 * @param isAll 是否把结果全部返回
 * @returns T
 */
export const fetchResultHandle = async (res: Response, isAll?: boolean) => {
  const parsedRes = await parseRes(res);

  if (res.ok) {
    if (isAll) {
      return parsedRes;
    }
    const { code, data, message } = parsedRes;
    if (code === '0000') {
      return data;
    } else {
      message && ElNotification({ type: 'error', message });
      throw message;
    }
  } else {
    const { code, data, message } = parsedRes;

    if (code === '0001') {
      let errMsg = '校验异常';
      if (data && data.length > 0) errMsg = `${data[0].field}${data[0].message}`;
      ElNotification({ type: 'error', message: errMsg });
      throw message;
    } else {
      message && ElNotification({ type: 'error', message });
      throw message;
    }
  }
};
